#include "kernel/chemsys.hpp"
#include "yocto/lua/lua-config.hpp"

ChemSys:: ~ChemSys() throw() {}


ChemSys:: ChemSys( const Library &lib) :
N(0),
M(0),
reactions_(),
library(lib),
Kproc(),
X(),
Gamma(),
dtGam(),
Nu(),
NuP(),
NuR(),
Phi(),
W(),
iW(),
lss(),
Xi(),
Chi(),
dX(),
drvs(),
dt(1e-4),
ftol(1e-7)
{
	
}

void ChemSys:: append( const Reaction &r ) 
{
	if( & r.library  != & library )
		throw exception("*** ChemSys(library mismatch)");
	
	if( ! reactions_.insert(r) )
	{
		throw exception("*** ChemSys(Multiple '%s')", r.name.c_str() );
	}
	
}

void ChemSys:: load_data( const array<Real> &F )
{
	if( F.size() < M ) throw exception("ChemSys::load_data(bad size)");
	for( size_t i=M;i>0;--i) X[i] = F[i];
}

void ChemSys:: save_data( array<Real> &F ) const
{
	if( F.size() < M ) throw exception("ChemSys::save_data(bad size)");
	for( size_t i=M;i>0;--i) F[i] = X[i];
}


const Reaction & ChemSys:: operator[]( const string &id ) const
{
	const Reaction *r = reactions_.search( id );
	if( !r )
		throw exception("No Reaction '%s'", id.c_str());
	return *r;
}

const Reaction & ChemSys:: operator[]( const char *id ) const
{
	const string __id = id;
	return (*this)[ __id ];
}



static inline
string __make_k( const string &name )
{
	string s;
	for( size_t i = 0; i <name.size(); ++i )
	{
		char C = name[i];
		switch( C )
		{
			case ' ':
			case '.':
			case '/':
			case '\\':
				C = '_';
				break;
				
			default:
				break;
		}
		s.append(C);
	}
	s += "_K";
	
	return s;
}

void ChemSys:: load( lua_State *L, const string &chemsys_name )
{
	assert(L);
	std::cerr << "---------------- Loading ChemSys '" << chemsys_name << "' ----------------" << std::endl;
	
	//==========================================================================
	// fetch the table
	//==========================================================================
	lua_settop(L, 0);
	lua_getglobal(L, chemsys_name.c_str() );
	if( !lua_istable(L,-1) )
		throw exception("*** ChemSys: '%s' is not a table", chemsys_name.c_str());
	
	const size_t n = lua_objlen(L, -1);
	std::cerr << "-- Found " << n << " reaction(s)" << std::endl;
	
	//==========================================================================
	// parse the table of reactions
	//==========================================================================
	string k_code; //! to be compiled
	for( size_t i=1; i <= n; ++i )
	{
		//======================================================================
		// get the reaction
		//======================================================================
		lua_rawgeti(L, -1, i);
		if( !lua_istable(L,-1) )
		{
			throw exception("*** reaction #%u is not a table!", unsigned(i) );
		}
		
		//======================================================================
		// parse the reaction
		//======================================================================
		
		const size_t nr = lua_objlen(L,-1);
		
		//----------------------------------------------------------------------
		// name of the reaction: { nil } breaks !
		//----------------------------------------------------------------------
		lua_rawgeti(L, -1, 1);
		if( lua_isnil(L,-1) )
			break;
		
		if( nr < 3 )
			throw exception("*** reaction #%u: requires at least name, constant, { item }", unsigned(i));
		
		if( !lua_isstring(L,-1) )
			throw exception("*** invalid type for reaction #%u name", unsigned(i));
		size_t       s_len = 0;
		const char  *s_txt = lua_tolstring(L,-1,&s_len);
		const string r_name(s_txt,s_len);
		std::cerr << "-- reaction <" << r_name << ">: ";
		lua_pop(L,1);
		
		//----------------------------------------------------------------------
		// constant of the reaction = function of (t)
		//----------------------------------------------------------------------
		lua_rawgeti(L, -1, 2);
		string k_name;
		switch( lua_type(L,-1) )
		{
			case LUA_TSTRING:
				s_len = 0;
				s_txt = lua_tolstring(L, -1, &s_len );
				k_name.assign(s_txt,s_len);
				break;
				
			case LUA_TNUMBER: {
				k_name = __make_k( r_name );
				k_code += "function ";
				k_code += k_name;
				k_code += "(t)\n\treturn ";
				k_code += vformat("%.12e\n", lua_tonumber(L, -1));
				k_code += "end\n";
			}
				break;
				
			default:
				throw exception("*** Reaction<%s>: invalid constant type", r_name.c_str() );
		}
		std::cerr << "K='" << k_name << "'" << std::endl;
		lua_pop(L,1);
		
		
		Reaction rxn( r_name, library, L, k_name );
		//----------------------------------------------------------------------
		// parse items = minitables ?
		//----------------------------------------------------------------------
		for(size_t j=3; j<=nr; ++j )
		{
			const unsigned ir = j-2;
			//-- get sub-table=Reaction::Item
			lua_rawgeti(L, -1, j);
			
			//-- first of item: an integer = nu
			lua_rawgeti(L, -1, 1);
			if( !lua_isnumber(L,-1) )
			{
				throw exception("*** Reaction<%s>: invalid coefficient type for item #%u", r_name.c_str(), ir );
			}
			const int nu = int( lua_tonumber(L, -1) );
			lua_pop(L,1);
			
			
			//-- second of item: a species
			lua_rawgeti(L, -1, 2);
			if( !lua_isstring(L,-1) )
			{
				throw exception("*** Reaction<%s>: invalid species type for item #%u", r_name.c_str(), ir );
			}
			s_len = 0;
			s_txt = lua_tolstring(L, -1, &s_len);
			const string id(s_txt,s_len);
			lua_pop(L,1);
			
			//-- append this to rxn
			rxn.append(nu,id);
			
			std::cerr << "-- \t|_ " << nu << " " << id << std::endl;
			
			//-- remove sub-table
			lua_pop(L,1);
		}
		
		if( rxn.items().size() <= 0 )
			throw exception("*** Reaction<%s>: no items!", r_name.c_str() );
		append( rxn );
		
		
		//======================================================================
		// done with the reaction
		//======================================================================
		lua_pop(L,1);
	}
	std::cerr << "-- compiling Lua code... " << std::endl << k_code << std::endl;
	Lua::Config::DoString(L,k_code);
	std::cerr << "-- ...success !" << std::endl;
	std::cerr << "-- Testing constants@time=0..." << std::endl;
	for( ReactionSet::iterator i = reactions_.begin(); i != reactions_.end(); ++i )
	{
		Reaction &rxn = *i;
		(void) rxn.K(0);
	}
	std::cerr << "-- ...everything seems fine!" << std::endl;
	std::cerr << "---------------- Loaded  ChemSys '" << chemsys_name << "' ----------------" << std::endl << std::endl;	
}


